/*
 * Copyright 2012-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.codestd.security.shiro.realm;

import com.codestd.security.mapper.SysUserMapper;
import com.codestd.security.model.SysUser;
import com.codestd.security.shiro.exception.CaptchaException;
import com.codestd.security.shiro.token.CaptchaUsernamePasswordToken;
import com.google.code.kaptcha.Constants;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 自定义凭证处理
 *
 * @author Wang Chengwei(Jaune)
 * @since 1.0.0
 */
public class DatabaseRealm extends AuthorizingRealm {

    @Autowired
    private SysUserMapper sysUserMapper;
    /**
     * 角色权限处理
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user = (SysUser) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        if (user != null) {
            List<String> permissions = this.sysUserMapper.getPermissions(user.getUserId());
            for (String permission : permissions) {
                authorizationInfo.addStringPermission(permission);
            }

            List<String> roles = this.sysUserMapper.getRoles(user.getUserId());
            for (String role : roles) {
                authorizationInfo.addRole(role);
            }
        }
        return authorizationInfo;
    }

    /**
     * 用户身份信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;

        this.checkCaptcha(token);

        SysUser user = this.sysUserMapper.findByUsernameOrPhoneNumber(usernamePasswordToken.getUsername());

        // 如果用户为空，则抛出用户不存在的异常
        if (user == null) {
            throw new UnknownAccountException();
        }

        // 判断用户状态
        if (UserState.LOCKED.is(user.getState())) {
            throw new LockedAccountException();
        } else if (UserState.DISABLED.is(user.getState())) {
            throw new DisabledAccountException();
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), user.getUsername());
        this.clearCachedAuthorizationInfo(authenticationInfo.getPrincipals());
        return authenticationInfo;
    }

    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        if (principals != null && principals.getPrimaryPrincipal() != null) {
            SysUser user = (SysUser) principals.getPrimaryPrincipal();
            return user.getUserId();
        } else {
            return super.getAuthorizationCacheKey(principals);
        }
    }

    /**
     * 如果验证码为空或验证码错误，则抛出CaptchaException
     */
    private void checkCaptcha(AuthenticationToken token) {
        CaptchaUsernamePasswordToken usernamePasswordToken = (CaptchaUsernamePasswordToken) token;
        // 判断是否需要校验验证码
        if (!usernamePasswordToken.isNeedCheckCaptcha()) {
            return;
        }
        String captcha = usernamePasswordToken.getCaptcha();
        if (captcha == null) {
            throw new CaptchaException();
        }
        Session session = SecurityUtils.getSubject().getSession();
        String sessionCaptcha = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
        if (!captcha.equals(sessionCaptcha)) {
            throw new CaptchaException();
        }
    }
}
